home *** CD-ROM | disk | FTP | other *** search
- /*
- * distfile.c - routines for reading/writing the distribution control file
- */
-
- #include "RevRdist.h"
- #include "dispatch.h"
- #include <string.h>
- #include <TransSkelProto.h>
- #include <TransDisplayProto.h>
-
- /*
- * iob - the structure used when reading the control file.
- * we do the I/O ourselves rather than include all of the stdio library.
- */
-
- struct iob
- {
- int size; /* the size of the buffer */
- int cnt; /* number of bytes in the buffer */
- int idx; /* index of next byte in buffer */
- Integer refNum; /* file reference number */
- Byte buf[1]; /* I/O buffer */
- };
-
-
- static OSErr getDActions ();
- OSErr getDActions (StringPtr, actions_t *);
- static OSErr getDField ();
- OSErr getDField (StringPtr, int *, int, StringPtr);
- static OSErr getDType ();
- OSErr getDType (tnode_t *, StringPtr);
- static OSErr openDistFile ();
- OSErr openDistFile (struct iob *);
- static OSErr readDLine ();
- OSErr readDLine (struct iob *, StringPtr);
- static void skipSpaces ();
- void skipSpaces (StringPtr, int *);
-
-
- /*
- *=========================================================================
- * freeDist (node) - free dist_node tree
- * entry: node = ptr to root of tree
- *=========================================================================
- */
- void
- freeDist (node)
- dnode_t *node;
- {
- dnode_t *parent, *child, *prev, *next;
- tnode_t *tp, *pp, *np;
-
- if (node == nil)
- return;
- /*
- * First, unlink this node from any sibling chain
- * (If our parent's child pointer is nil, skip this.)
- */
- if ((parent = node->parentp))
- {
- if ((prev = parent->childp))
- {
- if (prev == node)
- /* node is first child */
- parent->childp = node->sibp;
- else
- /* scan sib list for node */
- for (next = prev->sibp; next; prev = next, next = next->sibp)
- if (next == node)
- {
- prev->sibp = node->sibp;
- break;
- }
- node->sibp = nil;
- }
- }
- /*
- * Next, recursively free any children
- */
- for (next = node->childp, node->childp = nil; next; )
- {
- prev = next;
- next = next->sibp;
- freeDist (prev);
- }
- /*
- * Free any type_nodes back to parent's
- */
- pp = parent ? parent->tlistp : nil;
- for (tp = node->tlistp; tp && tp != pp; tp = np)
- {
- np = tp->morep;
- DisposPtr ((Ptr) tp);
- }
- /*
- * Finally, free selves
- */
- if (node->altname)
- DisposPtr ((Ptr) node->altname);
- DisposPtr ((Ptr) node);
- }
-
-
-
- /*
- *=========================================================================
- * parseDistFile () - build a dist_node tree from the control file
- * entry: This is a dispatcher routine.
- * The calling argument pointer is ignored.
- * returns: nil on failure, reason in ClueID, etc
- * ptr to dist_node tree if file read correctly.
- *
- * There are five formats for lines in the control file:
- * - Comments, which begin with #
- * - Folder starts, which begin with > and look like
- * > Foldername :Folderactions [:Alternate name]
- * - Folder ends, which begin with < and look like
- * < Foldername :DefaultActionsForFolder
- * - Files, which begin with | and look like
- * | Filename :Fileactions [:Alternate name]
- * - Type/creator specifications, which begin with * and look like
- * * Type :Actions
- * * (Creator) :Actions
- * or * Type(Creator) :Actions
- * where Type is a four character file type and Creator is a four
- * character file creator signature.
- *=========================================================================
- */
-
- DISPATCHED (parseDistFile)
- {
- struct lm /* local memory */
- {
- frame_t f;
- struct iob * iob;
- int ecnt; /* error count */
- int lcnt; /* control file line counter */
- dnode_t * head; /* ptr to base of tree */
- dnode_t * parent; /* parent node */
- dnode_t * prev; /* previous node in sibling chain */
- dnode_t * next; /* next node in sibling chain */
- };
- typedef struct lm lm_t;
- register lm_t *m; /* ptr to local memory */
- OSErr error; /* serious error number */
- int idx; /* index into lineBuf */
- register dnode_t * node; /* working node */
- int len; /* length of current line */
- Ptr p; /* temp pointer */
- Ptr paramv[2]; /* args/return values */
- int sel; /* type of control file entry */
- StringPtr str; /* string temp */
- tnode_t * tp; /* current type_node */
- actions_t actions; /* action list for current node */
- Str255 lineBuf; /* buffer for line from control file */
- Str255 nBuf; /* current component name */
- Str255 tBuf; /* temp buffer */
-
- error = 0;
- m = *(lm_t **)fh;
- switch (request)
- {
- case R_INIT:
- /*
- * Initial call: just see if we can get the memory we will need
- * and open the control file.
- */
- if (error = resizeFrame (fh, sizeof (lm_t)))
- break;
- p = NewPtr (sizeof (struct iob) + 512 - 1);
- HLock ((Handle)fh);
- m = *(lm_t **)fh;
- m->iob = (struct iob *) p;
- if (p == nil)
- {
- Clue1 = (SP) "\pNewPtr";
- error = MemError();
- break;
- }
- m->iob->size = 512;
- m->iob->cnt = m->iob->idx = 0;
- m->iob->refNum = 0;
- error = openDistFile (m->iob);
- if (error)
- break;
-
- m->f.state = 1;
- HUnlock ((Handle)fh);
- return R_CONT;
-
- case R_CONT:
- /*
- * We have only one "continue" state, and in that state we
- * process one line from the control file per call
- */
- if (m->f.state != 1)
- {
- Clue0 = (SP) "\pparseDistFile";
- NumToString ((long)m->f.state, Mbuf);
- panic (true, E_STATE, NullStr, Mbuf, nil);
- return R_QUIT;
- }
-
- error = readDLine (m->iob, lineBuf);
- if (error)
- break;
- m->lcnt++;
- if (Flags & DB_ECHODIST)
- {
- DisplayInt (m->lcnt);
- DisplayString (lineBuf);
- DisplayLn ();
- }
- len = lineBuf[0];
- idx = 1;
- skipSpaces (lineBuf, &idx);
- /*
- * skip empty lines and lines beginning with #
- */
- if (idx > len || lineBuf[idx] == '#')
- break;
- /*
- * see which type of line we have
- */
- sel = lineBuf[idx++];
- if (sel != '<' && sel != '|' && sel != '>' && sel != '*')
- {
- error = E_PREFIX;
- synerr:
- m->ecnt++;
- GetIndString (tBuf, ERR_STR, error);
- DisplayString (tBuf);
- DisplayInt (m->lcnt);
- DISPLAY ("\p : ");
- DisplayString (lineBuf);
- DISPLAY ("\p\r");
- error = 0;
- break;
- }
- /*
- * extract the file/folder name or type/creator
- */
- if (getDField (lineBuf, &idx, ':', nBuf))
- {
- error = E_NOFNAME;
- goto synerr;
- }
- if (sel != '*' && strchr ((char *)nBuf+1, ':'))
- {
- error = E_BADNAME;
- goto synerr;
- }
- /*
- * extract and interpret the actions list
- */
- if (getDField (lineBuf, &idx, ' ', tBuf))
- {
- error = E_NOACTION;
- goto synerr;
- }
- if (getDActions (tBuf, &actions))
- {
- error = E_BADACTION;
- goto synerr;
- }
- /*
- * extract the server alternate name field, if present
- */
- if (getDField (lineBuf, &idx, 0x100, tBuf) == 0)
- {
- if (tBuf[0] < 2 || tBuf[1] != '=')
- {
- error = E_ALTNAME;
- goto synerr;
- }
- }
- /*
- * Now that we've parsed the line into its pieces, use the
- * pieces to fill in its dist_node
- */
- switch (sel)
- {
- case '<':
- /*
- * end of folder contents
- * verify that there is a folder for us to match,
- * then copy actions to folder default entry
- * and unnest a level
- */
- if (m->parent == nil)
- {
- error = E_LAST;
- break;
- }
- if ((node = m->parent->childp) == nil
- || node->d_type != D_FOLDERDEF
- || node->name[0] != 0
- )
- {
- error = E_ENDFERR;
- goto emexit;
- }
- node->actions = actions;
- node = node->parentp;
- m->parent = node->parentp;
- m->prev = m->next = node;
- break;
-
- case '|':
- if (m->head == nil)
- {
- error = E_FIRST;
- break;
- }
- /* fall into ... */
- case '>':
- /*
- * for files and folders, create a new node, fill it in,
- * and link it in alphabetically
- */
- node = (dnode_t *) NewPtr (sizeof (dnode_t));
- if (node == nil)
- {
- error = MemError();
- break;
- }
- ZEROAT(node);
- node->parentp = m->parent;
- node->d_type = sel == '|' ? D_FILE : D_FOLDER;
- node->actions = actions;
- COPYPS (nBuf, node->name);
- if (m->head == nil)
- {
- m->head = node;
- m->prev = m->next = node;
- }
- else
- {
- if (m->parent == nil || m->prev == nil)
- {
- DisposPtr ((Ptr) node);
- error = E_DISTBAD;
- emexit:
- /*
- * Here for error where showing the bad control file line
- * makes sense
- */
- COPYPS (lineBuf, Mbuf);
- Clue1 = Mbuf;
- break;
- }
- /*
- * insert alphabetically in sibling chain
- */
- if (RelString (nBuf, m->prev->name, false, true) < 0)
- m->prev = m->next = m->parent->childp;
- for (; m->next; m->prev = m->next, m->next = m->next->sibp)
- if (RelString (nBuf, m->next->name, false, true) < 0)
- break;
- node->sibp = m->next;
- m->prev->sibp = node;
- m->prev = node;
- }
- /*
- * If there is an alternate name, copy it to alloc'ed space
- * (except for the leading = )
- */
- if ((len = tBuf[0]))
- {
- if ((node->altname = (StringPtr) NewPtr (len)) == nil)
- {
- if (!(error = MemError()))
- error = memFullErr;
- break;
- }
- BlockMove (tBuf+1, node->altname, (long) len);
- node->altname[0] = len - 1;
- }
- /*
- * If a file, done.
- */
- if (sel == '|')
- break;
- /*
- * Propagate our parent's type_node list
- */
- if (node->parentp)
- node->tlistp = node->parentp->tlistp;
- /*
- * Allocate a dummy child which will hold the folder defaults
- */
- m->next = (dnode_t *) NewPtr (sizeof (dnode_t));
- if (m->next == nil)
- {
- if (!(error = MemError()))
- error = memFullErr;
- break;
- }
- ZEROAT(m->next);
- m->next->parentp = node;
- m->next->d_type = D_FOLDERDEF;
- node->childp = m->next;
- m->parent = node;
- m->prev = m->next;
- break;
-
- case '*':
- /*
- * File type/creator selection.
- * Allocate a type_list node and link it in to the current
- * folder dist_node.
- */
- node = m->parent;
- if (node == nil)
- {
- error = E_FIRST;
- break;
- }
- tp = (tnode_t *) NewPtr (sizeof (tnode_t));
- if (tp == nil)
- {
- error = memFullErr;
- break;
- }
- ZEROAT (tp);
- error = getDType (tp, nBuf); /* fill in type/creator */
- if (error)
- {
- DisposPtr ((Ptr) tp);
- break;
- }
- tp->actions = actions; /* fill in actions */
- tp->morep = node->tlistp;
- node->tlistp = tp;
- break;
- }
- break;
-
- case R_BACKOUT:
- case R_QUIT:
- error = E_QUIT; /* magic error number */
- break;
-
- default:
- error = E_REQUEST; /* cannot happen */
-
- }
- /*
- * If this call had a serious error or if the end of the file was
- * reached and there were previous minor (syntax) errors, clean up
- * and return an error to the caller.
- * Otherwise, pop back to caller, returning the head of the tree we built.
- * In any case, close the file and return the iob resources.
- */
- if (error == 0)
- {
- /*
- * if error is not set, we are not done.
- */
- return R_CONT;
- }
- if (GetHandleSize ((Handle)fh) < sizeof (lm_t))
- return (popCall (E_QUIT, nil));
- /*
- * EOF error is not a real error. It is how we know we are done.
- */
- if (error == eofErr)
- {
- error = 0;
- /*
- * Check that start and end folders balanced out
- */
- if (m->parent)
- error = E_LAST;
- }
- if (error == 0 && m->ecnt)
- error = E_DISTL;
- if (m->iob)
- {
- file_info_t * fi;
-
- fi = &File_list[FL_DIST];
- if (m->iob->refNum)
- {
- (void) FSClose (m->iob->refNum);
- if (m->iob->refNum == fi->f_ref)
- fi->f_ref = 0;
- }
- DisposPtr ((Ptr) m->iob);
- m->iob = 0;
- }
- if (error)
- {
- ClueID = error;
- Clue0 = (SP) "\pparseDistFile";
- freeDist (m->head);
- m->head = 0;
- }
- paramv[0] = (Ptr) m->head;
- return (popCall (request==R_INIT ? R_CONT : request, paramv));
- }
-
-
- /*
- *=========================================================================
- * getDActions (str, ap) - decode action list
- * entry: str = string with action list text
- * ap = ptr to action structure to set from action list
- * returns: 0 if no error, else non-zero
- *=========================================================================
- */
- static
- OSErr
- getDActions (str, ap)
- register StringPtr str;
- register actions_t * ap;
- {
- register int c;
- register int i;
- register int len;
- int opt;
-
- setmem ((char *) ap, sizeof (*ap), A_DEFAULT);
- for (i = 0, len = str[0]; i+1 < len; )
- {
- c = str[++i]; /* letter for which condition */
- if (c >= 'a' && c <= 'z')
- c = c - 'a' + 'A';
- opt = c;
- c = str[++i]; /* letter for which action */
- if (c >= 'A' && c <= 'Z')
- c = c - 'A' + 'a';
- switch (c)
- {
- case '.': c = A_DEFAULT; break;
- case '-': c = A_PASS; break;
- case 'i': c = A_IGNORE; break;
- case 'j': if (opt == 'H' || opt == 'L')
- c = A_IGNORE;
- else
- c = A_JUNK;
- break;
- case 'd': c = A_DISCARD; break;
- case 'u': c = A_UPDATE; break;
- case 's': if (opt == 'H' || opt == 'L')
- {
- c = A_JUNK; break;
- }
- /* else fall into */
- default: return E_BADACTION;
- }
- switch (opt)
- {
- case 'C': ap->ifclient = c; break;
- case 'S': ap->ifserver = c; break;
- case 'V': ap->ifcreate = c; break;
- case 'N': ap->ifnewer = c; break;
- case 'O': ap->ifolder = c; break;
- case 'Z': ap->ifsize = c; break;
- case 'E': ap->otherwise = c; break;
- case 'W': ap->copywindow = c; break;
- case 'H': ap->invisible = c; break;
- case 'L': ap->locked = c; break;
- case 'A': ap->ifclient = c;
- ap->ifserver = c;
- ap->ifcreate = c;
- ap->ifnewer = c;
- ap->ifolder = c;
- ap->ifsize = c;
- ap->otherwise = c;
- ap->copywindow = c;
- ap->invisible = c;
- ap->locked = c;
- break;
- default: return E_BADACTION;
- }
- }
- return 0;
- }
-
-
-
- /*
- *=========================================================================
- * getDField (src, idx, term, dst) - extract field from line
- * where a field is delimited by term character or end of line
- * entry: src = ptr to line
- * idx = ptr to current index into src line
- * term = character to use to terminate field
- * special values:
- * ' ' -> terminate on whitespace
- * ':' -> terminate on ':' and also strip trailing whitespace
- * dst = ptr to string to receive field
- * returns: 0 if field found, idx updated
- * <> 0 if no field remaining in line
- *=========================================================================
- */
- static
- int
- getDField (src, idx, term, dst)
- register StringPtr src;
- int * idx;
- int term;
- register StringPtr dst;
- {
- register int len;
- register int i, j;
- register int c;
- int k; /* tracks last non-whitespace */
-
- skipSpaces (src, idx); /* skip leading spaces */
- len = src[0];
- i = *idx;
- j = 0;
- k = j;
- dst[j] = 0;
- if (i > len)
- return -1; /* no field data present */
- while (i <= len)
- {
- c = src[i++];
- if (c == '\\' && i <= len) /* allow \ to quote chars */
- {
- c = src[i++];
- k = j + 1;
- }
- else
- {
- if (c == term)
- break;
- if (term == ' ' && c == '\t')
- break;
- }
- dst[++j] = c;
- if (c != ' ' && c != '\t')
- k = j;
- }
- *idx = i;
- if (c == ':')
- j = k;
- dst[j+1] = 0;
- dst[0] = j;
- return 0;
- }
-
-
-
- /*
- *=========================================================================
- * getDType (tp, sp) - Parse TYPE and CREATOR string
- * entry: tp = ptr to type_node to fill in
- * sp = ptr to string with type and creator
- * returns: 0 if no error, else error number
- *=========================================================================
- */
-
- OSErr
- getDType (tp, sp)
- register tnode_t * tp;
- register StringPtr sp;
- {
- register int len; /* chars in sp */
- OSErr error;
-
- #define LT sizeof(tp->ftype) /* length of a file type */
- #define LC sizeof(tp->fcreator) /* length of a file creator */
-
- error = E_BADTYPE; /* assume failure */
- len = sp[0]; /* string length */
- switch (len)
- {
- case LT: /* just TYPE */
- BlockMove (sp+1, (Ptr)&tp->ftype, LT);
- error = 0;
- break;
-
- case 1+LC+1: /* just (CREATOR) */
- if (sp[1] == '(' && sp[len] == ')')
- {
- BlockMove (sp+2, (Ptr)&tp->fcreator, LC);
- error = 0;
- }
- break;
-
- case LT+1+LC+1: /* TYPE(CREATOR) */
- if (sp[LT+1] == '(' && sp[len] == ')')
- {
- BlockMove (sp+1, (Ptr)&tp->ftype, LT);
- BlockMove (sp+1+LT+1, (Ptr)&tp->fcreator, LC);
- error = 0;
- }
- break;
- }
- return error;
- }
-
-
-
- /*
- *=========================================================================
- * openDistFile (iob) - locate and open the control file
- * entry: iob = pointer to iob structure
- * Ap_file global names the control file
- * exit: returns 0 if file opened,
- * else error number
- *=========================================================================
- */
- static
- OSErr
- openDistFile (iob)
- struct iob * iob;
- {
- OSErr error;
- file_info_t * fi;
- Integer ref;
- StringHandle sh;
- HParamBlockRec pb;
-
- iob->cnt = 0;
- iob->idx = 0;
- fi = &File_list[FL_DIST];
- if (ref = fi->f_ref)
- {
- iob->refNum = ref;
- return 0;
- }
- sh = fi->f_path;
- HLock ((Handle) sh);
- ZERO (pb);
- pb.fileParam.ioNamePtr = *sh;
- pb.fileParam.ioVRefNum = fi->f_vol;
- pb.ioParam.ioPermssn = fsRdPerm;
- error = PBHOpen (&pb, false);
- if (error == 0)
- ref = pb.ioParam.ioRefNum;
- else
- {
- Clue0 = (SP) "\popenDistFile";
- Clue1 = (SP) "\pFSOpen";
- Clue2 = *sh;
- ref = 0;
- }
- fi->f_ref = iob->refNum = ref;
- return error;
- }
-
-
- /*
- *=========================================================================
- * readDLine (iob, str) - read line from file into string
- * entry iob = ptr to I/O control structure
- * str = string to receive line
- *=========================================================================
- */
- static
- OSErr
- readDLine (iob, str)
- register struct iob * iob;
- register StringPtr str;
- {
- register int c; /* current character */
- Longint count; /* bytes read */
- OSErr error;
- register int len; /* valid bytes in I/O buffer */
- register int i, j; /* indexes into buffer, str */
-
- error = 0;
- len = iob->cnt;
- i = iob->idx;
- j = 0;
- c = 0; /* set not in quote character */
- for (;;)
- {
- if (i >= len)
- {
- /*
- * if buffer exhausted, read some more
- */
- count = iob->size;
- error = FSRead (iob->refNum, &count, iob->buf);
- iob->cnt = len = count;
- i = 0;
- if (error == eofErr)
- error = 0;
- if (error)
- {
- ClueID = error;
- Clue0 = (SP) "\preadDLine";
- Clue1 = (SP) "\pFSRead";
- break;
- }
- if (len <= 0)
- {
- if (j == 0)
- error = eofErr; /* if nothing left, return eof */
- break; /* if didn't get anything, quit */
- }
- continue;
- }
- if (c == -1)
- str[++j] = c = iob->buf[i++]; /* copy quoted char exactly */
- else
- {
- c = iob->buf[i++];
- if (c == '\n' || c == '\r')
- break; /* exit on end of line */
- str[++j] = c;
- if (c == '\\')
- c = -1; /* flag next char quoted */
- }
- if (j >= 255)
- break;
- }
- iob->idx = i;
- str[0] = j;
- if (j < 255)
- str[j+1] = 0;
- return error;
- }
-
-
-
- /*
- *=========================================================================
- * skipSpaces (src, idx) - skip leading spaces in string
- * entry: src = ptr to string
- * idx = ptr to current index in string
- * exit idx advanced past any leading whitespace in string
- *=========================================================================
- */
- static
- void
- skipSpaces (src, idx)
- register StringPtr src;
- register int * idx;
- {
- register int len;
- register int c;
- register int i;
-
- len = src[0];
- i = *idx;
- while (i <= len)
- {
- c = src[i];
- if (c != ' ' && c != '\t')
- break;
- ++i;
- }
- *idx = i;
- }